home *** CD-ROM | disk | FTP | other *** search
- {$R-,S-,I-,B-,F-,O+}
-
- {---------------------------------------------------------
- BIOS disk I/O routines for floppy drives. Supports DOS
- real mode, DOS protected mode, and Windows. Requires
- TP6, TPW, or BP7.
-
- All functions are for floppy disks only; no hard drives.
-
- See the individual types and functions in the interface of
- this unit for more information. See the FMT.PAS sample
- program for an example of formatting disks.
-
- For status code definitions, see the implementation of
- function GetStatusStr.
-
- ---------------------------------------------------------
- Based on a unit provided by Henning Jorgensen of Denmark.
- Modified and cleaned up by TurboPower Software for pmode
- and Windows operation.
-
- TurboPower Software
- P.O. Box 49009
- Colorado Springs, CO 80949-9009
-
- CompuServe: 76004,2611
-
- Version 1.0 10/25/93
- Version 1.1 10/29/93
- fix a dumb bug in the MediaArray check
- ---------------------------------------------------------}
-
- unit BDisk;
- {-BIOS disk I/O routines for floppy drives}
-
- interface
-
- const
- MaxRetries : Byte = 3; {Number of automatic retries for
- read, write, verify, format}
-
- type
- DriveNumber = 0..7; {Acceptable floppy drive numbers}
- {Generally, 0 = A, 1 = B}
-
- DriveType = 0..4; {Floppy drive or disk types}
- {0 = unknown or error
- 1 = 360K
- 2 = 1.2M
- 3 = 720K
- 4 = 1.44M}
-
- VolumeStr = String[11]; {String for volume labels}
-
- FormatAbortFunc = {Prototype for format abort func}
- function (Track : Byte; {Track number being formatted, 0..MaxTrack}
- MaxTrack : Byte; {Maximum track number for this format}
- Kind : Byte {0 = format beginning}
- {1 = formatting Track}
- {2 = verifying Track}
- {3 = writing boot and FAT}
- {4 = format ending, Track = format status}
- ) : Boolean; {Return True to abort format}
-
-
- procedure ResetDrive(Drive : DriveNumber);
- {-Reset drive system (function $00). Call after any other
- disk function fails}
-
-
- function GetDiskStatus : Byte;
- {-Get status of last int $13 operation (function $01)}
-
-
- function GetStatusStr(ErrNum : Byte) : String;
- {-Return message string for any of the status codes used by
- this unit.}
-
-
- function GetDriveType(Drive : DriveNumber) : DriveType;
- {-Get drive type (function $08). Note that this returns the
- type of the *drive*, not the type of the diskette in it.
- GetDriveType returns 0 for an invalid drive.}
-
-
- function AllocBuffer(var P : Pointer; Size : Word) : Boolean;
- {-Allocate a buffer useable in real and protected mode.
- Buffers passed to ReadSectors and WriteSectors in pmode
- *MUST* be allocated by using this function. AllocBuffer returns
- False if sufficient memory is not available. P is also set to
- nil in that case.}
-
-
- procedure FreeBuffer(P : Pointer; Size : Word);
- {-Free buffer allocated by AllocBuffer. Size must match the
- size originally passed to AllocBuffer. FreeBuffer does
- nothing if P is nil.}
-
-
- function ReadSectors(Drive : DriveNumber;
- Track, Side, SSect, NSect : Byte;
- var Buffer) : Byte;
- {-Read absolute disk sectors (function $02). Track, Side,
- and SSect specify the location of the first sector to
- read. NSect is the number of sectors to read. Buffer
- must be large enough to hold these sectors. ReadSectors
- returns a status code, 0 for success.}
-
-
- function WriteSectors(Drive : DriveNumber;
- Track, Side, SSect, NSect : Byte;
- var Buffer) : Byte;
- {-Write absolute disk sectors (function $03). Track, Side,
- and SSect specify the location of the first sector to
- write. NSect is the number of sectors to write. Buffer
- must contain all the data to write. WriteSectors
- returns a status code, 0 for success.}
-
-
- function VerifySectors(Drive : DriveNumber;
- Track, Side, SSect, NSect : Byte) : Byte;
- {-Verify absolute disk sectors (function $04). This
- tests a computed CRC with the CRC stored along with the
- sector. Track, Side, and SSect specify the location of
- the first sector to verify. NSect is the number of
- sectors to verify. VerifySectors returns a status code,
- 0 for success. Don't call VerifySectors on PC/XTs and
- PC/ATs with a BIOS from 1985. It will overwrite the
- stack.}
-
-
- function FormatDisk(Drive : DriveNumber; DType : DriveType;
- Verify : Boolean; MaxBadSects : Byte;
- VLabel : VolumeStr;
- FAF : FormatAbortFunc) : Byte;
- {-Format drive that contains a disk of type DType. If Verify
- is True, each track is verified after it is formatted.
- MaxBadSects specifies the number of sectors that can be
- bad before the format is halted. If VLabel is not an
- empty string, FormatDisk puts the BIOS-level volume
- label onto the diskette. It does *not* add a DOS-level
- volume label. FAF is a user function hook that can be
- used to display status during the format, and to abort
- the format if the user so chooses. Parameters passed to
- this function are described in FormatAbortFunc above.
- FormatDisk also writes a boot sector and empty File
- Allocation Tables for the disk. FormatDisk returns a
- status code, 0 for success.}
-
-
- function EmptyAbortFunc(Track : Byte; MaxTrack : Byte; Kind : Byte) : Boolean;
- {-Do-nothing abort function for FormatDisk}
-
- {========================================================================}
-
- implementation
-
- uses
- {$IFDEF DPMI}
- WinApi,
- Dos;
- {$DEFINE pmode}
- {$ELSE}
- {$IFDEF Windows}
- WinApi,
- WinDos;
- {$DEFINE pmode}
- {$ELSE}
- Dos;
- {$UNDEF pmode}
- {$ENDIF}
- {$ENDIF}
-
- {$IFDEF Windows}
- type
- Registers = TRegisters;
- DateTime = TDateTime;
- {$ENDIF}
-
- type
- DiskRec =
- record
- SSZ : Byte; {Sector size}
- SPT : Byte; {Sectors/track}
- TPD : Byte; {Tracks/disk}
- SPF : Byte; {Sectors/FAT}
- DSC : Byte; {Directory sectors}
- FID : Byte; {Format id for FAT}
- BRD : array[0..13] of Byte; {Variable boot record data}
- end;
- DiskRecs = array[1..4] of DiskRec;
- SectorArray = array[0..511] of Byte;
-
- const
- DData : DiskRecs = {BRD starts at offset 13 of FAT}
- ((SSZ : $02; SPT : $09; TPD : $27; SPF : $02; DSC : $07; FID : $FD; {5.25" - 360K}
- BRD : ($02, $01, $00, $02, $70, $00, $D0, $02, $FD, $02, $00, $09, $00, $02)),
- (SSZ : $02; SPT : $0F; TPD : $4F; SPF : $07; DSC : $0E; FID : $F9; {5.25" - 1.2M}
- BRD : ($01, $01, $00, $02, $E0, $00, $60, $09, $F9, $07, $00, $0F, $00, $02)),
- (SSZ : $02; SPT : $09; TPD : $4F; SPF : $03; DSC : $07; FID : $F9; {3.50" - 720K}
- BRD : ($02, $01, $00, $02, $70, $00, $A0, $05, $F9, $03, $00, $09, $00, $02)),
- (SSZ : $02; SPT : $12; TPD : $4F; SPF : $09; DSC : $0E; FID : $F0; {3.50" - 1.44M}
- BRD : ($01, $01, $00, $02, $E0, $00, $40, $0B, $F0, $09, $00, $12, $00, $02)));
-
- BootRecord : SectorArray = {Standard boot program}
- ($EB, $34, $90, $41, $4D, $53, $54, $20, $33, $2E, $30, $00, $02, $01, $01, $00, $02, $E0, $00, $40, $0B, $F0, $09, $00,
- $12, $00, $02, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $12,
- $00, $00, $00, $00, $01, $00, $FA, $33, $C0, $8E, $D0, $BC, $00, $7C, $16, $07, $BB, $78, $00, $36, $C5, $37, $1E, $56,
- $16, $53, $BF, $2B, $7C, $B9, $0B, $00, $FC, $AC, $26, $80, $3D, $00, $74, $03, $26, $8A, $05, $AA, $8A, $C4, $E2, $F1,
- $06, $1F, $89, $47, $02, $C7, $07, $2B, $7C, $FB, $CD, $13, $72, $67, $A0, $10, $7C, $98, $F7, $26, $16, $7C, $03, $06,
- $1C, $7C, $03, $06, $0E, $7C, $A3, $3F, $7C, $A3, $37, $7C, $B8, $20, $00, $F7, $26, $11, $7C, $8B, $1E, $0B, $7C, $03,
- $C3, $48, $F7, $F3, $01, $06, $37, $7C, $BB, $00, $05, $A1, $3F, $7C, $E8, $9F, $00, $B8, $01, $02, $E8, $B3, $00, $72,
- $19, $8B, $FB, $B9, $0B, $00, $BE, $D6, $7D, $F3, $A6, $75, $0D, $8D, $7F, $20, $BE, $E1, $7D, $B9, $0B, $00, $F3, $A6,
- $74, $18, $BE, $77, $7D, $E8, $6A, $00, $32, $E4, $CD, $16, $5E, $1F, $8F, $04, $8F, $44, $02, $CD, $19, $BE, $C0, $7D,
- $EB, $EB, $A1, $1C, $05, $33, $D2, $F7, $36, $0B, $7C, $FE, $C0, $A2, $3C, $7C, $A1, $37, $7C, $A3, $3D, $7C, $BB, $00,
- $07, $A1, $37, $7C, $E8, $49, $00, $A1, $18, $7C, $2A, $06, $3B, $7C, $40, $38, $06, $3C, $7C, $73, $03, $A0, $3C, $7C,
- $50, $E8, $4E, $00, $58, $72, $C6, $28, $06, $3C, $7C, $74, $0C, $01, $06, $37, $7C, $F7, $26, $0B, $7C, $03, $D8, $EB,
- $D0, $8A, $2E, $15, $7C, $8A, $16, $FD, $7D, $8B, $1E, $3D, $7C, $EA, $00, $00, $70, $00, $AC, $0A, $C0, $74, $22, $B4,
- $0E, $BB, $07, $00, $CD, $10, $EB, $F2, $33, $D2, $F7, $36, $18, $7C, $FE, $C2, $88, $16, $3B, $7C, $33, $D2, $F7, $36,
- $1A, $7C, $88, $16, $2A, $7C, $A3, $39, $7C, $C3, $B4, $02, $8B, $16, $39, $7C, $B1, $06, $D2, $E6, $0A, $36, $3B, $7C,
- $8B, $CA, $86, $E9, $8A, $16, $FD, $7D, $8A, $36, $2A, $7C, $CD, $13, $C3, $0D, $0A, $4E, $6F, $6E, $2D, $53, $79, $73,
- $74, $65, $6D, $20, $64, $69, $73, $6B, $20, $6F, $72, $20, $64, $69, $73, $6B, $20, $65, $72, $72, $6F, $72, $0D, $0A,
- $52, $65, $70, $6C, $61, $63, $65, $20, $61, $6E, $64, $20, $73, $74, $72, $69, $6B, $65, $20, $61, $6E, $79, $20, $6B,
- $65, $79, $20, $77, $68, $65, $6E, $20, $72, $65, $61, $64, $79, $0D, $0A, $00, $0D, $0A, $44, $69, $73, $6B, $20, $42,
- $6F, $6F, $74, $20, $66, $61, $69, $6C, $75, $72, $65, $0D, $0A, $00, $49, $4F, $20, $20, $20, $20, $20, $20, $53, $59,
- $53, $4D, $53, $44, $4F, $53, $20, $20, $20, $53, $59, $53, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
- $00, $00, $00, $00, $00, $00, $55, $AA);
-
- MediaArray : array[DriveType, 1..2] of Byte =
- (($00, $00), {Unknown disk}
- ($01, $02), {360K disk}
- ($00, $03), {1.2M disk}
- ($00, $04), {720K disk}
- ($00, $04)); {1.44M disk}
-
- {$IFDEF pmode}
- type
- DPMIRegisters =
- record
- DI : LongInt;
- SI : LongInt;
- BP : LongInt;
- Reserved : LongInt;
- BX : LongInt;
- DX : LongInt;
- CX : LongInt;
- AX : LongInt;
- Flags : Word;
- ES : Word;
- DS : Word;
- FS : Word;
- GS : Word;
- IP : Word;
- CS : Word;
- SP : Word;
- SS : Word;
- end;
-
- function GetRealSelector(RealPtr : Pointer; Limit : Word) : Word;
- {-Set up a selector to point to RealPtr memory}
- type
- OS =
- record
- O, S : Word;
- end;
- var
- Status : Word;
- Selector : Word;
- Base : LongInt;
- begin
- GetRealSelector := 0;
- Selector := AllocSelector(0);
- if Selector = 0 then
- Exit;
- {Assure a read/write selector}
- Status := ChangeSelector(CSeg, Selector);
- Base := (LongInt(OS(RealPtr).S) shl 4)+LongInt(OS(RealPtr).O);
- if SetSelectorBase(Selector, Base) = 0 then begin
- Selector := FreeSelector(Selector);
- Exit;
- end;
- Status := SetSelectorLimit(Selector, Limit);
- GetRealSelector := Selector;
- end;
-
- procedure GetRealIntVec(IntNo : Byte; var Vector : Pointer); Assembler;
- asm
- mov ax,0200h
- mov bl,IntNo
- int 31h
- les di,Vector
- mov word ptr es:[di],dx
- mov word ptr es:[di+2],cx
- end;
-
- function RealIntr(IntNo : Byte; var Regs : DPMIRegisters) : Word; Assembler;
- asm
- xor bx,bx
- mov bl,IntNo
- xor cx,cx {StackWords = 0}
- les di,Regs
- mov ax,0300h
- int 31h
- jc @@ExitPoint
- xor ax,ax
- @@ExitPoint:
- end;
- {$ENDIF}
-
- procedure Int13Call(var Regs : Registers);
- {-Call int $13 for real or protected mode}
- {$IFDEF pmode}
- var
- Base : LongInt;
- DRegs : DPMIRegisters;
- {$ENDIF}
- begin
- {$IFDEF pmode}
- {This pmode code is valid only for the AH values used in this unit}
- FillChar(DRegs, SizeOf(DPMIRegisters), 0);
- DRegs.AX := Regs.AX;
- DRegs.BX := Regs.BX;
- DRegs.CX := Regs.CX;
- DRegs.DX := Regs.DX;
- case Regs.AH of
- 2, 3, 5 :
- {Calls that use ES as a buffer segment}
- begin
- Base := GetSelectorBase(Regs.ES);
- if (Base <= 0) or (Base > $FFFF0) then begin
- Regs.Flags := 1;
- Regs.AX := 1;
- Exit;
- end;
- DRegs.ES := Base shr 4;
- end;
- end;
- if RealIntr($13, DRegs) <> 0 then begin
- Regs.Flags := 1;
- Regs.AX := 1;
- end else begin
- Regs.Flags := DRegs.Flags;
- Regs.AX := DRegs.AX;
- Regs.BX := DRegs.BX; {BX is returned by GetDriveType function only}
- end;
-
- {$ELSE}
- Intr($13, Regs);
- {$ENDIF}
- end;
-
- function GetDriveType(Drive : DriveNumber) : DriveType;
- var
- Regs : Registers;
- begin
- Regs.AH := $08;
- Regs.DL := Drive;
- Int13Call(Regs);
- if Regs.AH = 0 then
- GetDriveType := Regs.BL
- else
- GetDriveType := 0;
- end;
-
- function GetDiskStatus : Byte;
- var
- Regs : Registers;
- begin
- Regs.AH := $01;
- Int13Call(Regs);
- GetDiskStatus := Regs.AL;
- end;
-
- function GetStatusStr(ErrNum : Byte) : String;
- var
- NumStr : string[3];
- begin
- case ErrNum of
- {Following codes are defined by the floppy BIOS}
- $00 : GetStatusStr := '';
- $01 : GetStatusStr := 'Invalid command';
- $02 : GetStatusStr := 'Address mark not found';
- $03 : GetStatusStr := 'Disk write protected';
- $04 : GetStatusStr := 'Sector not found';
- $06 : GetStatusStr := 'Floppy disk removed';
- $08 : GetStatusStr := 'DMA overrun';
- $09 : GetStatusStr := 'DMA crossed 64KB boundary';
- $0C : GetStatusStr := 'Media type not found';
- $10 : GetStatusStr := 'Uncorrectable CRC error';
- $20 : GetStatusStr := 'Controller failed';
- $40 : GetStatusStr := 'Seek failed';
- $80 : GetStatusStr := 'Disk timed out';
-
- {Following codes are added by this unit}
- $FA : GetStatusStr := 'Format aborted';
- $FB : GetStatusStr := 'Invalid media type';
- $FC : GetStatusStr := 'Too many bad sectors';
- $FD : GetStatusStr := 'Disk bad';
- $FE : GetStatusStr := 'Invalid drive or type';
- $FF : GetStatusStr := 'Insufficient memory';
- else
- Str(ErrNum, NumStr);
- GetStatusStr := 'Unknown error '+NumStr;
- end;
- end;
-
- procedure ResetDrive(Drive : DriveNumber);
- var
- Regs : Registers;
- begin
- Regs.AH := $00;
- Regs.DL := Drive;
- Int13Call(Regs);
- end;
-
- function AllocBuffer(var P : Pointer; Size : Word) : Boolean;
- var
- L : LongInt;
- begin
- {$IFDEF pmode}
- L := GlobalDosAlloc(Size);
- if L <> 0 then begin
- P := Ptr(Word(L and $FFFF), 0);
- AllocBuffer := True;
- end else begin
- P := nil;
- AllocBuffer := False
- end;
- {$ELSE}
- if MaxAvail >= Size then begin
- GetMem(P, Size);
- AllocBuffer := True;
- end else begin
- P := nil;
- AllocBuffer := False;
- end;
- {$ENDIF}
- end;
-
- procedure FreeBuffer(P : Pointer; Size : Word);
- begin
- if P = nil then
- Exit;
- {$IFDEF pmode}
- Size := GlobalDosFree(LongInt(P) shr 16);
- {$ELSE}
- FreeMem(P, Size);
- {$ENDIF}
- end;
-
- function CheckParms(DType : DriveType; Drive : DriveNumber) : Boolean;
- {-Make sure drive and type are within range}
- begin
- CheckParms := False;
- if (DType < 1) or (DType > 4) then
- Exit;
- if (Drive > 7) then
- Exit;
- CheckParms := True;
- end;
-
- function SubfSectors(SubFunc : Byte;
- Drive : DriveNumber;
- Track, Side, SSect, NSect : Byte;
- var Buffer) : Byte;
- {-Code shared by ReadSectors, WriteSectors, VerifySectors, FormatTrack}
- var
- Tries : Byte;
- Done : Boolean;
- Regs : Registers;
- begin
- Tries := 1;
- Done := False;
- repeat
- Regs.AH := SubFunc;
- Regs.AL := NSect;
- Regs.CH := Track;
- Regs.CL := SSect;
- Regs.DH := Side;
- Regs.DL := Drive;
- Regs.ES := Seg(Buffer);
- Regs.BX := Ofs(Buffer);
- Int13Call(Regs);
-
- if Regs.AH <> 0 then begin
- ResetDrive(Drive);
- Inc(Tries);
- if Tries > MaxRetries then
- Done := True;
- end else
- Done := True;
- until Done;
-
- SubfSectors := Regs.AH;
- end;
-
- function ReadSectors(Drive : DriveNumber;
- Track, Side, SSect, NSect : Byte;
- var Buffer) : Byte;
- begin
- ReadSectors := SubfSectors($02, Drive, Track, Side, SSect, NSect, Buffer);
- end;
-
- function WriteSectors(Drive : DriveNumber;
- Track, Side, SSect, NSect : Byte;
- var Buffer) : Byte;
- begin
- WriteSectors := SubfSectors($03, Drive, Track, Side, SSect, NSect, Buffer);
- end;
-
- function VerifySectors(Drive : DriveNumber;
- Track, Side, SSect, NSect : Byte) : Byte;
- var
- Dummy : Byte;
- begin
- VerifySectors := SubfSectors($04, Drive, Track, Side, SSect, NSect, Dummy);
- end;
-
- function SetDriveTable(DType : DriveType) : Boolean;
- {-Set drive table parameters for formatting}
- var
- P : Pointer;
- DBSeg : Word;
- DBOfs : Word;
- begin
- SetDriveTable := False;
-
- {$IFDEF pmode}
- GetRealIntVec($1E, P);
- DBSeg := GetRealSelector(P, $FFFF);
- if DBSeg = 0 then
- Exit;
- DBOfs := 0;
- {$ELSE}
- GetIntVec($1E, P);
- DBSeg := LongInt(P) shr 16;
- DBOfs := LongInt(P) and $FFFF;
- {$ENDIF}
-
- {Set gap length for formatting}
- case DType of
- 1 : Mem[DBSeg:DBOfs+7] := $50; {360K}
- 2 : Mem[DBSeg:DBOfs+7] := $54; {1.2M}
- 3,
- 4 : Mem[DBSeg:DBOfs+7] := $6C; {720K or 1.44M}
- end;
-
- {Set max sectors/track}
- Mem[DBSeg:DBOfs+4] := DData[DType].SPT;
-
- {$IFDEF pmode}
- DBSeg := FreeSelector(DBSeg);
- {$ENDIF}
-
- SetDriveTable := True;
- end;
-
- function GetMachineID : Byte;
- {-Return machine ID code}
- {$IFDEF pmode}
- var
- SegFFFF : Word;
- {$ENDIF}
- begin
- {$IFDEF pmode}
- SegFFFF := GetRealSelector(Ptr($FFFF, $0000), $FFFF);
- if SegFFFF = 0 then
- GetMachineID := 0
- else begin
- GetMachineID := Mem[SegFFFF:$000E];
- SegFFFF := FreeSelector(SegFFFF);
- end;
- {$ELSE}
- GetMachineID := Mem[$FFFF:$000E];
- {$ENDIF}
- end;
-
- function IsATMachine : Boolean;
- {-Return True if AT or better machine}
- begin
- IsATMachine := False;
- if Lo(DosVersion) >= 3 then
- case GetMachineId of
- $FC, $F8 : {AT or PS/2}
- IsATMachine := True;
- end;
- end;
-
- function GetChangeLineType(Drive : DriveNumber; var CLT : Byte) : Byte;
- {-Return change line type of drive}
- var
- Regs : Registers;
- begin
- Regs.AH := $15;
- Regs.DL := Drive;
- Int13Call(Regs);
- if (Regs.Flags and FCarry) <> 0 then begin
- GetChangeLineType := Regs.AH;
- CLT := 0;
- end else begin
- GetChangeLineType := 0;
- CLT := Regs.AH;
- end;
- end;
-
- function SetFloppyType(Drive : DriveNumber; FType : Byte) : Byte;
- {-Set floppy type for formatting}
- var
- Tries : Byte;
- Done : Boolean;
- Regs : Registers;
- begin
- Tries := 1;
- Done := False;
- repeat
- Regs.AH := $17;
- Regs.AL := FType;
- Regs.DL := Drive;
- Int13Call(Regs);
- if Regs.AH <> 0 then begin
- ResetDrive(Drive);
- Inc(Tries);
- if Tries > MaxRetries then
- Done := True;
- end else
- Done := True;
- until Done;
-
- SetFloppyType := Regs.AH;
- end;
-
- function SetMediaType(Drive : DriveType; TPD : Byte; SPT : Byte) : Byte;
- {-Set media type for formatting}
- var
- Regs : Registers;
- begin
- Regs.AH := $18;
- Regs.DL := Drive;
- Regs.CH := TPD;
- Regs.CL := SPT;
- Int13Call(Regs);
- SetMediaType := Regs.AH;
- end;
-
- function FormatDisk(Drive : DriveNumber; DType : DriveType;
- Verify : Boolean; MaxBadSects : Byte;
- VLabel : VolumeStr;
- FAF : FormatAbortFunc) : Byte;
- label
- ExitPoint;
- type
- CHRNRec =
- record
- CTrack : Byte; {Track 0..?}
- CSide : Byte; {Side 0..1}
- CSect : Byte; {Sector 1..?}
- CSize : Byte; {Size 0..?}
- end;
- CHRNArray = array[1..18] of CHRNRec;
- FATArray = array[0..4607] of Byte;
- var
- Tries : Byte;
- Track : Byte;
- Side : Byte;
- Sector : Byte;
- RWritten : Byte;
- RTotal : Byte;
- FatNum : Byte;
- BadSects : Byte;
- ChangeLine : Byte;
- DiskType : Byte;
- Status : Byte;
- Done : Boolean;
- Trash : Word;
- DT : DateTime;
- VDate : LongInt;
- Regs : Registers;
- BootPtr : ^SectorArray;
- CHRN : ^CHRNArray;
- FATs : ^FATArray;
-
- procedure MarkBadSector(Track, Side, Sector : Byte);
- const
- BadMark = $FF7; {Bad cluster mark}
- var
- CNum : Integer; {Cluster number}
- FOfs : Word; {Offset into fat for this cluster}
- FVal : Word; {FAT value for this cluster}
- OFVal : Word; {Old FAT value for this cluster}
- begin
- CNum := (((((Track*2)+Side)*DData[DType].SPT)+Sector-RTotal-2) div
- DData[DType].BRD[0])+2;
- if CNum > 1 then begin
- {Sector is in data space}
- FOfs := (CNum*3) div 2;
- Move(FATs^[FOfs], FVal, 2);
- if Odd(CNum) then
- OFVal := (FVal and (BadMark shl 4))
- else
- OFVal := (FVal and BadMark);
- if OFVal = 0 then begin
- {Not already marked bad, mark it}
- if Odd(CNum) then
- FVal := (FVal or (BadMark shl 4))
- else
- FVal := (FVal or BadMark);
- Move(FVal, FATs^[FOfs], 2);
- {Add to bad sector count}
- Inc(BadSects, DData[DType].BRD[0]);
- end;
- end;
- end;
-
- begin
- {Validate parameters. Can't do anything unless these are reasonable}
- if not CheckParms(DType, Drive) then
- Exit;
-
- {Initialize buffer pointers in case of failure}
- FATs := nil;
- CHRN := nil;
- BootPtr := nil;
-
- {Status proc: starting format}
- if FAF(0, DData[DType].TPD, 0) then begin
- Status := $FA;
- goto ExitPoint;
- end;
-
- {Error code for invalid drive or media type}
- Status := $FE;
-
- case GetDriveType(Drive) of
- 1 : {360K drive formats only 360K disks}
- if DType <> 1 then
- goto ExitPoint;
- 2 : {1.2M drive formats 360K or 1.2M disk}
- if DType > 2 then
- goto ExitPoint;
- 3 : {720K drive formats only 720K disks}
- if DType <> 3 then
- goto ExitPoint;
- 4 : {1.44M drive formats 720K or 1.44M disks}
- if Dtype < 3 then
- goto ExitPoint;
- else
- goto ExitPoint;
- end;
-
- {Error code for out-of-memory or DPMI error}
- Status := $FF;
-
- {Allocate buffers}
- if not AllocBuffer(Pointer(FATs), SizeOf(FATArray)) then
- goto ExitPoint;
- if not AllocBuffer(Pointer(CHRN), SizeOf(CHRNArray)) then
- goto ExitPoint;
- if not AllocBuffer(Pointer(BootPtr), SizeOf(BootRecord)) then
- goto ExitPoint;
-
- {Initialize boot record}
- Move(BootRecord, BootPtr^, SizeOf(BootRecord));
- Move(DData[DType].BRD, BootPtr^[13], 14);
-
- {Initialize the FAT table}
- FillChar(FATs^, SizeOf(FATArray), 0);
- FATs^[0] := DData[DType].FID;
- FATs^[1] := $FF;
- FATs^[2] := $FF;
-
- {Set drive table parameters by patching drive table in memory}
- if not SetDriveTable(DType) then
- goto ExitPoint;
-
- {On AT class machines, set format parameters via BIOS}
- if IsATMachine then begin
- {Get change line type: 1 -> 360K drive, 2 -> 1.2M or 3.5" drive}
- Status := GetChangeLineType(Drive, ChangeLine);
- if Status <> 0 then
- goto ExitPoint;
- if (ChangeLine < 1) or (ChangeLine > 2) then begin
- Status := 1;
- goto ExitPoint;
- end;
-
- {Determine floppy type for SetFloppyType call}
- DiskType := MediaArray[DType, ChangeLine];
- if DiskType = 0 then begin
- Status := $FB;
- goto ExitPoint;
- end;
-
- {Set floppy type for drive}
- Status := SetFloppyType(Drive, DiskType);
- if Status <> 0 then
- goto ExitPoint;
-
- {Set media type for format}
- Status := SetMediaType(Drive, DData[DType].TPD, DData[DType].SPT);
- if Status <> 0 then
- goto ExitPoint;
- end;
-
- {Format each sector}
- ResetDrive(Drive);
- BadSects := 0;
-
- for Track := 0 to DData[DType].TPD do begin
- {Status proc: formatting track}
- if FAF(Track, DData[DType].TPD, 1) then begin
- Status := $FA;
- goto ExitPoint;
- end;
-
- for Side := 0 to 1 do begin
- {Initialize CHRN for this sector}
- for Sector := 1 to DData[DType].SPT do
- with CHRN^[Sector] do begin
- CTrack := Track;
- CSide := Side;
- CSect := Sector;
- CSize := DData[DType].SSZ;
- end;
-
- {Format this sector, with retries}
- Status := SubfSectors($05, Drive, Track, Side,
- 1, DData[DType].SPT, CHRN^);
- if Status <> 0 then
- goto ExitPoint;
- end;
-
- if Verify then begin
- {Status proc: verifying track}
- if FAF(Track, DData[DType].TPD, 2) then begin
- Status := $FA;
- goto ExitPoint;
- end;
-
- for Side := 0 to 1 do
- {Verify the entire track}
- if VerifySectors(Drive, Track, Side,
- 1, DData[DType].SPT) <> 0 then begin
- if Track = 0 then begin
- {Disk bad}
- Status := $FD;
- goto ExitPoint;
- end;
-
- for Sector := 1 to DData[DType].SPT do
- if VerifySectors(Drive, Track, Side,
- Sector, 1) <> 0 then begin
- MarkBadSector(Track, Side, Sector);
- if BadSects > MaxBadSects then begin
- Status := $FC;
- goto ExitPoint;
- end;
- end;
- end;
- end;
- end;
-
- {Status proc: writing boot and FAT}
- if FAF(0, DData[DType].TPD, 3) then begin
- Status := $FA;
- goto ExitPoint;
- end;
-
- {Write boot record}
- Status := WriteSectors(Drive, 0, 0, 1, 1, BootPtr^);
- if Status <> 0 then begin
- Status := $FD;
- goto ExitPoint;
- end;
-
- {Write FATs and volume label}
- Track := 0;
- Side := 0;
- Sector := 2;
- FatNum := 0;
- RTotal := (2*DData[DType].SPF)+DData[DType].DSC;
- for RWritten := 0 to RTotal-1 do begin
- if Sector > DData[DType].SPT then begin
- Sector := 1;
- Inc(Side);
- end;
-
- if RWritten < (2*DData[DType].SPF) then begin
- if FatNum > DData[DType].SPF-1 then
- FatNum := 0;
- end else begin
- FillChar(FATs^, 512, 0);
- if ((VLabel <> '') and (RWritten = 2*DData[DType].SPF)) then begin
- {Put in volume label}
- for Trash := 1 to Length(VLabel) do
- VLabel[Trash] := Upcase(VLabel[Trash]);
- while Length(VLabel) < 11 do
- VLabel := VLabel+' ';
- Move(VLabel[1], FATs^, 11);
- FATs^[11] := 8;
- GetDate(DT.Year, DT.Month, DT.Day, Trash);
- GetTime(DT.Hour, DT.Min, DT.Sec, Trash);
- PackTime(DT, VDate);
- Move(VDate, FATs^[22], 4);
- end;
- FatNum := 0;
- end;
-
- if WriteSectors(Drive, Track, Side,
- Sector, 1, FATs^[FatNum*512]) <> 0 then begin
- Status := $FD;
- goto ExitPoint;
- end;
-
- Inc(Sector);
- Inc(FatNum);
- end;
-
- {Success}
- Status := 0;
-
- ExitPoint:
- FreeBuffer(BootPtr, SizeOf(BootRecord));
- FreeBuffer(CHRN, SizeOf(CHRNArray));
- FreeBuffer(FATs, SizeOf(FATArray));
-
- {Status proc: ending format}
- Done := FAF(Status, DData[DType].TPD, 4);
- FormatDisk := Status;
- end;
-
- function EmptyAbortFunc(Track, MaxTrack : Byte; Kind : Byte) : Boolean;
- begin
- EmptyAbortFunc := False;
- end;
-
- end.
-
- { ------------------------------- DEMO PROGRAM -------------------- }
- { ------------------------------- CUT HERE ---------------------}
-
- {$R-,S-,I-}
-
- program Fmt;
- {-Simple formatting program to demonstate DISKB unit}
-
- uses
- {$IFDEF Windows}
- WinCrt,
- {$ENDIF}
- BDisk;
-
- const
- ESC = #27;
- CR = #13;
-
- type
- CharSet = set of Char;
-
- var
- DLet : Char;
- DTyp : Char;
- Verf : Char;
- GLet : Char;
- DNum : Byte;
- Status : Byte;
- VStr : VolumeStr;
-
- const
- DriveTypeName : array[DriveType] of string[5] =
- ('other', '360K', '1.2M', '720K', '1.44M');
-
- {$IFNDEF Windows}
- function ReadKey : Char; assembler;
- {-Low budget readkey routine}
- asm
- xor ah,ah
- int 16h
- end;
- {$ENDIF}
-
- function GetKey(Prompt : String; OKSet : CharSet) : Char;
- {-Get and return a key in the OKSet}
- var
- Ch : Char;
- begin
- Write(Prompt);
- repeat
- Ch := Upcase(ReadKey);
- if Ch = ESC then begin
- WriteLn;
- Halt;
- end;
- until (Ch in OKSet);
- if Ch <> CR then
- Write(Ch);
- WriteLn;
- GetKey := Ch;
- end;
-
- function AbortFunc(Track, MaxTrack : Byte; Kind : Byte) : Boolean; far;
- {-Display formatting status. Could check for abort here too}
- begin
- case Kind of
- 0 : {Format beginning}
- Write('Formatting ');
- 1 : {Formatting track}
- Write(^H^H^H^H, ((Track*100) div MaxTrack):3, '%');
- 2 : {Verifying track}
- Write(^H, 'V');
- 3 : {Writing boot and FAT}
- Write(^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H, 'Writing boot and FAT');
- 4 : {Format ending}
- begin
- Write(^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H);
- {Track returns final status code in this case}
- if Track = 0 then
- WriteLn('Formatted successfully')
- else
- WriteLn('Format failed: ', GetStatusStr(Track));
- end;
- end;
- AbortFunc := False;
- end;
-
- begin
- WriteLn('Floppy Formatter: <Esc> to exit');
-
- {Get formatting parameters}
- DLet := GetKey('Drive to format? (A or B): ', ['A'..'B']);
- DTyp := GetKey('Disk type? (1=360K, 2=1.2M, 3=720K, 4=1.44M): ', ['1'..'4']);
- Verf := GetKey('Verify? (Y or N) ', ['N', 'Y']);
- Write('Volume label? ');
- ReadLn(VStr);
- GLet := GetKey('Insert disk and press <Enter> ', [#13]);
-
- {Compute drive number}
- DNum := Byte(DLet)-Byte('A');
-
- WriteLn('Drive type is ', DriveTypeName[GetDriveType(DNum)]);
-
- Status := FormatDisk(DNum, {drive number}
- Byte(DTyp)-Byte('0'), {format type}
- (Verf = 'Y'), {verify?}
- 10, {max bad sectors}
- VStr, {volume label}
- AbortFunc); {abort function}
- {AbortFunc reports the status}
- end.